package com.spring.framework.spel;

import org.junit.Test;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.expression.BeanFactoryResolver;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.ParserContext;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;

import java.lang.reflect.Method;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;

/**
 * Spring Expression
 *
 * @author xuweizhi
 * @since 2020/12/15 16:39
 */
@SuppressWarnings("all")
public class HelloSpEl {

	@Test
	public void helloSpel() {
		//SPEL解析器  实例是可重用的，线程安全。
		ExpressionParser parser = new SpelExpressionParser();

		//分析表达式字符串并返回可用于重复计算的 Expression 对象。
		Expression exp = parser.parseExpression("'Hello SPEL'");
		//在默认标准上下文中计算此表达式，并返回计算结果
		String message = (String) exp.getValue();
		System.out.println(message);
	}

	/**
	 * SPEL也有自己的容器EvaluationContext，又被称为“计算上下文”，用于计算表达式以解析属性、方法或字段并帮助执行类型转换
	 */
	@Test
	public void context() {
		ExpressionParser parser = new SpelExpressionParser();

		String rootObject = "rootObject";

		//手动设置上下文，获取上下文容器
		StandardEvaluationContext context = new StandardEvaluationContext();
		//向容器设置变量
		context.setVariable("rootObject", rootObject);
		context.setVariable("xxx", "字符串变量");
		//传递容器，通过#引用变量作为一次计算表达式的根对象RootObject
		System.out.println(parser.parseExpression("#xxx").getValue(context));
		System.out.println(parser.parseExpression("#rootObject.length()").getValue(context));

		//可以为一个容器设置RootObject，引用它的属性时，可以不加#前缀，一个容器只有一个RootObject
		context.setRootObject(rootObject);
		System.out.println(parser.parseExpression("isEmpty()").getValue(context));


		//getValue方法如果不传递容器中将使用默认计算上下文容器，传递的rootObject变量就是容器要使用来计算表达式的根对象，这种方法也不需要#引用变量
		System.out.println(parser.parseExpression("length()").getValue(rootObject, Integer.class));
	}

	@Test
	public void literalExpressions() {
		ExpressionParser parser = new SpelExpressionParser();
		//字符串 包裹在''中
		System.out.println(parser.parseExpression("'Hello World'").getValue());
		//字符串中的单引号应该使用两个''表示
		System.out.println(parser.parseExpression("'Hello'' World'").getValue());
		//字符串中的其他特殊符号应该使用相应的转义字符
		System.out.println(parser.parseExpression("'\" \t \\ \377 \u0024'").getValue());


		//数值
		System.out.println(parser.parseExpression("111111111").getValue());
		System.out.println(parser.parseExpression("111.1").getValue());
		//转换的类型都是包装类型，不能直接进行基本类型的转换。
		//System.out.println((int)parser.parseExpression("111.1").getValue());
		//整数类型不可超出int范围。
		//System.out.println((int)parser.parseExpression("-1111111111111111").getValue());
		//科学计数法 默认转换为Double
		System.out.println((double) parser.parseExpression("6.0221415E+23").getValue());
		//16进制 默认转换为Integer
		System.out.println((int) parser.parseExpression("0x7FFFFFFF").getValue());


		//boolean
		System.out.println((boolean) parser.parseExpression("true").getValue());
		//null
		System.out.println(parser.parseExpression("null").getValue() == null);
		System.out.println((int) 11.1);
	}

	/**
	 * SPEL支持对象属性导航（property Navigating），这类似于EL的属性导航，对于多层嵌套属性，可以使用“.”来连接，同时支持获取属性值和设置属性值。
	 * <p>
	 * 导航的属性，要求必须是public修饰的或者提供了相应的getter方法：getXxx，同时，路径上的属性值必须不能为null。
	 * <p>
	 * SPEL表达式中，属性的第一个字母可以不区分大小写。
	 */
	@Test
	public void propertyNavigating() {
		ExpressionParser parser = new SpelExpressionParser();
		SpelBean spelBean = new SpelBean(new Birth("china"));
		StandardEvaluationContext context = new StandardEvaluationContext();
		context.setVariable("spelBean", spelBean);
		//支持属性导航 要求属性必须是public修饰的或者提供了相应的getter方法
		System.out.println(parser.parseExpression("#spelBean.birth.birthplace").getValue(context));
		//当然支持设置属性值
		parser.parseExpression("#spelBean.Birth.Birthplace").setValue(context, "wenwen");
		//属性的第一个字母可以不区分大小写
		System.out.println(parser.parseExpression("#spelBean.Birth.Birthplace").getValue(context));
	}

	/**
	 * SPEL支持集合元素导航，array数组和Collection集合中元素内容是使用[index]表示法获得的，index表示索引。Map集合的value是通过[key]获取的，注意这里的key如果是字符串，那么需要加上’’包裹。也可以引用其他变量作为key。
	 * <p>
	 * array数组和Collection集合需要注意索引越界，将会抛出异常，map如果指定的key错误，那么会返回null。
	 * <p>
	 * 集合元素导航同时支持获取值和设置值。
	 */
	@Test
	public void collectionNavigating() {
		Object o = new Object();
		SpelBean spelBean = getSpelBean(o);
		ExpressionParser parser = new SpelExpressionParser();
		StandardEvaluationContext context = new StandardEvaluationContext();
		context.setVariable("spelBean", spelBean);
		context.setVariable("o", o);
		//支持集合导航

		//array和Collection使用[index]
		System.out.println(parser.parseExpression("#spelBean.strings[2]").getValue(context));
		System.out.println(parser.parseExpression("#spelBean.stringList[2]").getValue(context));
		System.out.println(parser.parseExpression("#spelBean.stringSet[2]").getValue(context));

		//map使用[key]
		//注意，我们的map中key是一个为"1"的字符串，因此这里需要带上''
		System.out.println(parser.parseExpression("#spelBean.objectObjectMap['1']").getValue(context));
		System.out.println(parser.parseExpression("#spelBean.objectObjectMap[2]").getValue(context));
		//key也可以引用内部变量作为key   返回结果如果在参数中指定为Optional类型，就不需要我们强转了
		Optional value = parser.parseExpression("#spelBean.objectObjectMap[#o]").getValue(context, Optional.class);
		System.out.println(value);

		System.out.println(parser.parseExpression("#spelBean.properties['1']").getValue(context));
		System.out.println(parser.parseExpression("#spelBean.properties['2']").getValue(context));

		System.out.println("------------设置属性值------------");

		parser.parseExpression("#spelBean.strings[2]").setValue(context, "newValue1");
		System.out.println(parser.parseExpression("#spelBean.strings[2]").getValue(context));
		parser.parseExpression("#spelBean.objectObjectMap['1']").setValue(context, "newValue2");
		System.out.println(parser.parseExpression("#spelBean.objectObjectMap['1']").getValue(context));
	}

	private SpelBean getSpelBean(Object o) {
		SpelBean spelBean = new SpelBean();
		String[] strings = new String[]{"1", "2", "4", "4"};
		List<String> stringList = Arrays.asList(strings);
		Set<String> stringSet = new HashSet<>(stringList);
		Map<Object, Object> objectObjectMap = new HashMap<>();
		objectObjectMap.put("1", 11);
		objectObjectMap.put(2, 22);
		objectObjectMap.put(3, 33);
		objectObjectMap.put(o, Optional.empty());
		Properties properties = new Properties();
		properties.setProperty("1", "111");
		properties.setProperty("2", "222");
		properties.setProperty("3", "333");
		properties.setProperty("4", "444");
		spelBean.setStrings(strings);
		spelBean.setStringList(stringList);
		spelBean.setStringSet(stringSet);
		spelBean.setObjectObjectMap(objectObjectMap);
		spelBean.setProperties(properties);
		return spelBean;
	}

	/**
	 * 可以使用{}和特定拆分符号,直接在表达式中直接表示列表（Inline Lists）。另外{}本身就是代表一个空的list集合，因此可以实现集合嵌套。
	 * <p>
	 * 一定要注意，对于常量数据组成的集合（即集合的元素没有引用到容器中的变量），如果我们设置预期返回类型为List，或者不设置预期返回类型，
	 * 那么实际上返回是一个java.util.Collections.UnmodifiableList的实例，该集合是不可变的，包括新增、删除、修改操作，都将直接抛出
	 * UnsupportedOperationException，只能用来遍历，这么做是为了提升SPEL的解析性能。因此，如果需要后续对集合进行操作，建议使用具体的
	 * 集合类型接收，比如ArrayLsit.class。
	 */
	@Test
	public void inlineList() {
		ExpressionParser parser = new SpelExpressionParser();
		List value = parser.parseExpression("{1,2,3,4}").getValue(List.class);
		System.out.println(value);
		System.out.println(value.getClass());

		//集合嵌套
		List value1 = parser.parseExpression("{{},{'a','b'},{'x','y'}}").getValue(ArrayList.class);
		System.out.println(value1);
		System.out.println(value1.getClass());
		//{}本身就是一个集合，可以实现集合嵌套
		for (Object o : value1) {
			System.out.println(o.getClass());
			List list0 = (List) o;
			System.out.println(list0);
		}

		//一定要注意，如果我们设置预期类型为List，或者不设置返回类型，那么实际上返回是一个java.util.Collections.UnmodifiableList的实例
		//该集合是不可变的，包括新增、删除、修改操作，都将直接抛出UnsupportedOperationException，只能用来遍历
		//因此，如果需要后续对集合进行操作，建议使用具体的集合类型接收，比如ArrayLsit
		//value.set(1, 2);
	}

	/**
	 * 我们还可以使用{key：value}表示法直接在表达式中直接表示map（Inline Maps），元素之间使用,分隔。另外{:}本身就是代表一个空的map集合，
	 * 因此可以实现集合嵌套，map的key或者value也可以是一个map。
	 * <p>
	 * 一定要注意，对于常量数据组成的集合（即集合的key或者value没有引用到容器中的变量），如果我们设置预期返回类型为List，或者不设置预期返回
	 * 类型，那么实际上返回是一个java.util.Collections.UnmodifiableMap的实例，该集合是不可变的，包括新增、删除、修改操作，都将直接抛出
	 * UnsupportedOperationException，只能用来遍历，这么做是为了提升SPEL的解析性能。因此，如果需要后续对集合进行操作，建议使用具体的集合
	 * 类型接收，比如HashMap.class（实际上返回LinkedHashMap）。
	 */
	@Test
	public void inlineMap() {
		StandardEvaluationContext context = new StandardEvaluationContext();
		context.setVariable("value", "value");
		ExpressionParser parser = new SpelExpressionParser();
		//常量数据集合
		System.out.println(parser.parseExpression("{1:2,2:3,3:4}").getValue(context, Map.class).getClass());
		//引用到容器变量的集合
		Map value = parser.parseExpression("{1:#value,2:3,3:4}").getValue(context, Map.class);
		System.out.println(value);
		System.out.println(value.getClass());

		System.out.println("--------map嵌套----------");
		//嵌套
		//指定HashMap类型，实际上返回的是一个LinkedHashMap类型
		HashMap value1 = parser.parseExpression("{{1:1,2:2}:2,2:3,3:{'xx':'y',{1,2}:4}}").getValue(HashMap.class);
		System.out.println(value1);
		System.out.println(value1.getClass());
		//{}本身就是一个集合，可以实现集合嵌套
		value1.forEach((k, v) -> {
			System.out.println(k);
			System.out.println(k.getClass());
			System.out.println(v);
			System.out.println(v.getClass());
		});

		// 一定要注意，对于常量数据组成的集合（即集合的key或者value没有引用到容器中的变量），如果我们设置预期返回类型为List，
		// 或者不设置预期返回类型，那么实际上返回是一个java.util.Collections.UnmodifiableMap的实例，该集合是不可变的，
		// 包括新增、删除、修改操作，都将直接抛出UnsupportedOperationException，只能用来遍历，这么做是为了提升SPEL的解析性能。
		// 因此，如果需要后续对集合进行操作，建议使用具体的集合类型接收，比如HashMap.class。
		value.put(1, 2);
	}

	@Test
	public void arrayConstruction() {
		ExpressionParser parser = new SpelExpressionParser();
		//初始化数组
		int[] numbers1 = (int[]) parser.parseExpression("new int[4]").getValue();
		System.out.println(Arrays.toString(numbers1));
		//数组可以自动的转换为集合
		System.out.println(parser.parseExpression("new int[4]").getValue(List.class));
		//集合也可以自动的转换为数组
		System.out.println(parser.parseExpression("{1,2,3,4}").getValue(int[].class));

		//初始化数组并赋值
		int[] numbers2 = (int[]) parser.parseExpression("new int[]{1,2,3,3}").getValue();
		System.out.println(Arrays.toString(numbers2));

		//数组可以自动的转换为集合
		System.out.println(parser.parseExpression("new int[]{1,2,3,3}").getValue(List.class));
		System.out.println(parser.parseExpression("new int[]{1,2,3,3}").getValue(Set.class));

		//二维数组
		int[][] numbers3 = (int[][]) parser.parseExpression("new int[4][5]").getValue();
		System.out.println(Arrays.deepToString(numbers3));
		//目前不支持初始化生成多维数组并赋值
		//int[][] numbers4 = (int[][]) parser.parseExpression("new int[][]{{1,2},{3,4},{3,4,5,6}}").getValue();
	}


	@Test
	public void methodInvoke() {
		ExpressionParser parser = new SpelExpressionParser();
		//对字符串调用方法
		//截取
		System.out.println(parser.parseExpression("'abc'.substring(1, 3)").getValue(String.class));
		//拆分数组
		System.out.println(Arrays.toString(parser.parseExpression("'a,b,c,c'.split(',')").getValue(String[].class)));
		//虽然split方法返回一个数组，实际上也可以转换为集合，自动转换机制
		System.out.println(parser.parseExpression("'a,b,c,c'.split(',')").getValue(Set.class));

		SpelBean spelBean = new SpelBean();
		spelBean.setProperty1("property1");
		spelBean.setProperty2(11);
		//默认Context调用方法
		System.out.println(parser.parseExpression("getProperty1()").getValue(spelBean, String.class));

		//指定Context调用方法
		StandardEvaluationContext context = new StandardEvaluationContext();
		context.setVariable("spelBean", spelBean);
		context.setRootObject(spelBean);
		System.out.println(parser.parseExpression("getProperty1()").getValue(context, String.class));
	}

	/**
	 * SPEL支持在数值之间使用算数运算符（Mathematical Operators）。加法（+）、减法（-）、乘法（*）、除法（/）、指数（^）、取模（%）运算符。
	 */
	@Test
	public void mathematicalOperators() {
		ExpressionParser parser = new SpelExpressionParser();
		System.out.println(parser.parseExpression("6+2").getValue());
		System.out.println(parser.parseExpression("6-2").getValue());
		System.out.println(parser.parseExpression("6*2").getValue());
		System.out.println(parser.parseExpression("6/2").getValue());
		System.out.println(parser.parseExpression("6%2").getValue());
		System.out.println(parser.parseExpression("6^2").getValue());
		System.out.println(parser.parseExpression("6+2/2-1*3").getValue());
		System.out.println(parser.parseExpression("2+6^2").getValue());
		//支持括号
		System.out.println(parser.parseExpression("(2+6)^2").getValue());
		//支持其它数值类型计算
		System.out.println(parser.parseExpression("1000.00 - 1e4").getValue());
		System.out.println(parser.parseExpression("2.0 * 3e0 * 4").getValue());
		System.out.println(parser.parseExpression("0x111 * 4").getValue());
		//连接字符串
		System.out.println(parser.parseExpression("'6'+2").getValue());
	}

	@Test
	public void relationalOperators() {
		ExpressionParser parser = new SpelExpressionParser();
		System.out.println(parser.parseExpression("6>2").getValue());
		System.out.println(parser.parseExpression("6>=2").getValue());
		System.out.println(parser.parseExpression("6<2").getValue());
		System.out.println(parser.parseExpression("6<=2").getValue());
		System.out.println(parser.parseExpression("6==2").getValue());
		System.out.println(parser.parseExpression("6!=2").getValue());
		//结合算数运算符
		System.out.println(parser.parseExpression("6!=2*3").getValue());
		System.out.println(parser.parseExpression("6+1!=2*3").getValue());
		System.out.println(parser.parseExpression("6+1>2*3").getValue());

		System.out.println("----null----");
		System.out.println(parser.parseExpression("0 gt null").getValue());
		System.out.println(parser.parseExpression("-11111>null").getValue());
		System.out.println(parser.parseExpression("null==null").getValue());
	}

	@Test
	public void logicalOperators() {
		ExpressionParser parser = new SpelExpressionParser();
		System.out.println(parser.parseExpression("'srt'.isEmpty() and ''.isEmpty()").getValue());
		System.out.println(parser.parseExpression("'srt'.isEmpty() && ''.isEmpty()").getValue());

		System.out.println(parser.parseExpression("'srt'.isEmpty() or ''.isEmpty()").getValue());
		System.out.println(parser.parseExpression("'srt'.isEmpty() || ''.isEmpty()").getValue());

		System.out.println(parser.parseExpression("not 'srt'.isEmpty()").getValue());
		System.out.println(parser.parseExpression("!'srt'.isEmpty()").getValue());

		System.out.println(parser.parseExpression("true || false ").getValue());
	}

	@Test
	public void elvisOperator() {
		ExpressionParser parser = new SpelExpressionParser();
		System.out.println(parser.parseExpression("'srt'.isEmpty()?'yes':'no'").getValue());
		System.out.println(parser.parseExpression("''.isEmpty()?'yes':'no'").getValue());
		//ELVIS运算符，如果满足关系，那么返回true，否则返回"no"
		System.out.println(parser.parseExpression("''.isEmpty()?:'no'").getValue());
		//支持嵌套
		System.out.println(parser.parseExpression("''.isEmpty()?''.isEmpty()?'是':'否':'no'").getValue());
	}

	/**
	 * SPEL使用赋值运算符（Assignment Operator）：=
	 * 通常我们在setValue中为变量或者变量的属性赋值，当然也可以在SPEL表达式中使用=来为变量或者变量的属性赋值。
	 */
	@Test
	public void assignmentOperator() {
		SpelBean spelBean = new SpelBean(new Birth("china"));
		spelBean.setProperty1("property1");
		StandardEvaluationContext context = new StandardEvaluationContext();
		context.setVariable("spelBean", spelBean);
		context.setVariable("xxx", "yyy");
		context.setRootObject(spelBean);
		ExpressionParser parser = new SpelExpressionParser();


		System.out.println(parser.parseExpression("birth.birthplace").getValue(context));
		//通常在setValue方法中为变量/属性赋值
		parser.parseExpression("birth.birthplace").setValue(context, "usa");
		System.out.println(parser.parseExpression("birth.birthplace").getValue(context));

		System.out.println(parser.parseExpression("property1").getValue(context));
		//也可以在SPEL表达式中为属性赋值
		System.out.println(parser.parseExpression("property1='ppp'").getValue(context));
		System.out.println(parser.parseExpression("property1").getValue(context));

		System.out.println(parser.parseExpression("#xxx").getValue(context));
		//也可以在SPEL表达式中为变量赋值
		System.out.println(parser.parseExpression("#xxx='zzz'").getValue(context));
		System.out.println(parser.parseExpression("#xxx").getValue(context));
	}

	@Test
	public void instanceofOperator() {
		ExpressionParser parser = new SpelExpressionParser();
		//是否属于Integer
		System.out.println(parser.parseExpression("'xyz' instanceof T(java.lang.Integer)").getValue());
		//Java的基础类（java.lang包）支持类名简写
		System.out.println(parser.parseExpression("'xyz' instanceof T(Integer)").getValue());
		//是否属于String
		System.out.println(parser.parseExpression("'xyz' instanceof T(String)").getValue());
		System.out.println(parser.parseExpression("'xyz' instanceof T(Object)").getValue());

		System.out.println(parser.parseExpression("11 instanceof T(Integer)").getValue());
		//不能使用基本类型当作类型判断，因为前面的1被立即装箱成为Integer对戏那个
		System.out.println(parser.parseExpression("11 instanceof T(int)").getValue());
	}

	@Test
	public void matchesOperator() {
		ExpressionParser parser = new SpelExpressionParser();
		System.out.println(parser.parseExpression("'xyz' matches '\\b\\w{3}\\b'").getValue());
		System.out.println(parser.parseExpression("'。。' matches '(.)\\1+'").getValue());
	}

	/**
	 * afe navigation operator来自groovy语言，用于避免抛出惹人烦的NullPointerException，实现安全失败的导航操作。
	 * <p>
	 * 当引用一个对象时，可能需要在访问该对象的方法或属性之前验证它不是空的，如果忘记了，那么会导致NullPointerException。
	 * 使用Safe Navigation操作符之后，如果引用的对象为null，那么将返回null而不是抛出异常。
	 * <p>
	 * safe navigation operator的语法很简单，就是在导航的.之前加上一个?即可。
	 */
	@Test
	public void safeNavigationOperator() {
		Birth birth = new Birth("Birth safeNavigationOperator");
		ExpressionParser parser = new SpelExpressionParser();
		//safe navigation operator的语法很简单，就是在导航的.之前加上一个?即可
		System.out.println(parser.parseExpression("birthplace?.replace(' ', '')?.split('a')").getValue(birth));
		System.out.println(parser.parseExpression("birthDate?.getYear()").getValue(birth));

		//下面的没有使用safe navigation operator的操作将会抛出异常
		System.out.println(parser.parseExpression("birthDate.getYear()").getValue(birth));
	}

	/**
	 * SPEL支持使用T(ClassPath)的样式来表示Java的某个类的class对象，同时又可调用该类的静态方法和字段。
	 * ClassPath一般填写类的全限定类名，java.lang包中的类只需要写简单类名即可。
	 */
	@Test
	public void tClass() {
		ExpressionParser parser = new SpelExpressionParser();
		//调用String的join静态方法
		System.out.println(parser.parseExpression("T(String).join('_','@','@')").getValue());
		//调用Math的random静态方法
		System.out.println(parser.parseExpression("T(Math).random()*100").getValue());
		//调用Integer的MAX_VALUE静态字段
		System.out.println(parser.parseExpression("T(Integer).MAX_VALUE").getValue());
	}

	/**
	 * 支持 new 对象
	 */
	@Test
	public void constructors() {
		ExpressionParser parser = new SpelExpressionParser();
		//java.lang 包中的类使用简单类名即可
		System.out.println(parser.parseExpression("new String('new 一个字符串')").getValue());
		System.out.println(parser.parseExpression("new Exception('new 一个异常')").getValue());

		//其他包中的类使用全路径类名
		//new 一个IoC对象
		System.out.println(parser.parseExpression("new org.springframework.context.support.ClassPathXmlApplicationContext()").getValue());
		//构造器中也可以new其他对象作为参数，使用其他语法
		//System.out.println(parser.parseExpression("new com.spring.spel.SpelBean('new 一个普通对象',new com.spring.spel.Birth('new对象',T(java.time.LocalDate).now(),T(java.time.LocalTime).now()))").getValue());
	}

	/**
	 * SPEL支持使用#variableName语法引用计算上下文容器中的变量（Variables）。变量是通过在EvaluationContext的实现并调用setVariable方法设置的。
	 * <p>
	 * 变量命名可以由小写字母（a-z）、大写字母（A-Z）、数字（0-9）、下划线（_）、美刀（$）这里几部分组成，第一个字符不能是数字。具有同名变量时，后
	 * 定义的变量值将会覆盖之前定义的变量值。
	 * <p>
	 * 引用到变量后，就可以利用变量执行前面所学的操作了，比如调用方法、属性等等操作。
	 */
	@Test
	public void variables() {
		Birth birth = new Birth("china");

		ExpressionParser parser = new SpelExpressionParser();

		StandardEvaluationContext context = new StandardEvaluationContext();
		context.setVariable("_variable1", "variable1");
		context.setVariable("variable2", "222");
		context.setVariable("variable2", "variable2");
		context.setVariable("birth", birth);

		System.out.println(parser.parseExpression("#_variable1").getValue(context));
		System.out.println(parser.parseExpression("#variable2").getValue(context));
		System.out.println(parser.parseExpression("#birth").getValue(context));
		System.out.println(parser.parseExpression("#birth.birthplace").getValue(context));
		System.out.println(parser.parseExpression("#birth.birthDate=T(java.time.LocalDate).now()").getValue(context));
		System.out.println(parser.parseExpression("#birth").getValue(context));
		System.out.println(parser.parseExpression("#this").getValue(context, birth));
	}

	/**
	 * #this和#root是预定义的变量。#this表示引用当前被就算的对象。
	 * <p>
	 * #root表示上下文容器的根对象rootObject，默认为null。我们可以通过context的setRootObject方法为一个容器设置一个rootObject，随后对该容器引
	 * 用它的相关属性、方法时，可以不使用#variableName引用，一个容器只有一个rootObject。设置了rootObject之后，直接使用#root和#this都是表示容
	 * 器的根对象的引用。
	 * <p>
	 * 另外，如果在getValue中手动设置了当前被评估的对象时，#root表示当前被评估的对象。
	 */
	@Test
	public void thisRoot() {
		Birth birth = new Birth("china");
		Birth birth2 = new Birth("usa");
		ExpressionParser parser = new SpelExpressionParser();
		StandardEvaluationContext context = new StandardEvaluationContext();

		//#this表示引用当前被评估的对象。
		System.out.println(parser.parseExpression("#this").getValue(birth));

		//#root默认为null
		System.out.println(parser.parseExpression("#root").getValue());

		//手动设置了当前被评估的对象时，#root表示当前被评估的对象。
		System.out.println(parser.parseExpression("#root").getValue(birth));
		System.out.println(parser.parseExpression("#root").getValue(birth2));


		context.setVariable("birth", birth);
		context.setVariable("birth2", birth2);

		//设置容器的root根对象。随后对该容器引用它的相关属性、方法时，可以不使用#variableName引用，#root就表示引用根对象
		context.setRootObject(birth);
		System.out.println(parser.parseExpression("#root").getValue(context));
		System.out.println(parser.parseExpression("birthplace").getValue(context));
		System.out.println(parser.parseExpression("birthDate=T(java.time.LocalDate).now()").getValue(context));
		System.out.println(parser.parseExpression("#root").getValue(context));


		//设置了rootObject之后，直接使用#this表示容器的根对象的引用
		System.out.println(parser.parseExpression("#this").getValue(context));

		//手动设置了当前被评估的对象时，#root表示当前被评估的对象。
		System.out.println(parser.parseExpression("#root").getValue(birth2));

		//#this更常被用来进行集合的筛选
		context.setVariable("primes", new ArrayList<>(Arrays.asList(2, 3, 5, 7, 11, 13, 17)));

		//使用selection 集合选择的语法 后面会讲到
		System.out.println(parser.parseExpression("#primes.?[#this>10]").getValue(context, List.class));
	}

	private static class TimeConverter {
		/**
		 * 需要被注册的"函数"
		 * 注意：只能是静态的方法才能通过函数引用方式调用
		 */
		private static String convert(byte type) {
			DateTimeFormatter dateTimeFormater;
			switch (type) {
				case 1:
					dateTimeFormater = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH时mm分ss秒");
					break;
				case 2:
					dateTimeFormater = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH时mm分ss秒 SSS毫秒");
					break;
				default:
					dateTimeFormater = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss SSS");
			}
			LocalDateTime localDateTime = LocalDateTime.now();
			return dateTimeFormater.format(localDateTime);
		}
	}

	/**
	 * 这里所说的函数，实际上就是Java中的Method类实例，是对方法的抽象，利用到了反射的技术，这个emethod实例被称为“函数引用”。注意，只能是静态的方法才能通过函数引用方式调用
	 */
	@Test
	public void functions() throws NoSuchMethodException {
		StandardEvaluationContext context = new StandardEvaluationContext();

		//通过反射需要被注册的"函数"
		Method convert = TimeConverter.class.getDeclaredMethod("convert", byte.class);

		//注册到计算上下文容器中 这两方法都行
		//context.setVariable("convert", convert);
		context.registerFunction("convert", convert);

		//调用函数 传入参数即可获取结果
		ExpressionParser parser = new SpelExpressionParser();
		System.out.println(parser.parseExpression("#convert(1)").getValue(context));
		System.out.println(parser.parseExpression("#convert(2)").getValue(context));
		System.out.println(parser.parseExpression("#convert(3)").getValue(context));
	}

	@Test
	public void beanReferences() {
		//初始化Spring容器，这里手动添加两个bean进取
		Birth birth = new Birth("Birth Bean References");
		SpelBean spelBean = new SpelBean("SpelBean Bean References", birth);
		ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext();
		ac.refresh();
		ConfigurableListableBeanFactory beanFactory = ac.getBeanFactory();
		beanFactory.registerSingleton("birth", birth);
		beanFactory.registerSingleton("spelBean", spelBean);


		StandardEvaluationContext context = new StandardEvaluationContext();
		//设置Bean解析器实例，这个BeanFactoryResolver就是Spring默认的解析器，
		//它接受一个IoC容器实例，在resolve方法中将会调用容器的getBean方法查找该容器中的所有bean。
		context.setBeanResolver(new BeanFactoryResolver(ac));


		//尝试通过 @beanname 引用bean
		ExpressionParser parser = new SpelExpressionParser();
		System.out.println(parser.parseExpression("@birth").getValue(context));
		System.out.println(parser.parseExpression("@birth.birthplace").getValue(context));
		System.out.println(parser.parseExpression("@spelBean").getValue(context));

		System.out.println("--------通过SPEL设置bean的值----------");
		System.out.println(ac.getBean("birth"));
		parser.parseExpression("@birth.birthDate").setValue(context, LocalDate.now());
		parser.parseExpression("@birth.birthDate").setValue(context, LocalDate.now());
		//容器中获取bean的实例并输出
		System.out.println(ac.getBean("birth"));
	}

	/**
	 * SPEL支持集合元素的筛选操作（Collection Selection），允许通过从源集合的元素中选择符合条件的元素转换为另一个集合。这类似于Java8的lambda的filter操作。语法格式如下：
	 * <p>
	 * .?[selectionExpression]，它将检索与选择表达式匹配的集合全部元素，返回这些匹配的原始元素的新集合。
	 * .^ [selectionExpression]，它将检索并获取与选择表达式匹配的第一个元素。对于本身无序的集合不能保证返回某一个确定的元素。对于map，返回的是一个map。
	 * .$[selectionExpression]，它将检索并获取与选择表达式匹配的最后一个元素。对于本身无序的集合不能保证返回某一个确定的元素。对于map，返回的是一个map。
	 * selectionExpression应该返回一个boolean类型的结果。array和Collection的[]
	 * 中的#this表示集合元素本身，map的[]中的#this同样表示集合元素，不过是Map.Entry，我们可以获取key或者value进行筛选。访问集合元素的属性、方法时，可以省略#this引用前缀。
	 */
	@Test
	public void collectionSelection() {
		SpelBean spelBean = new SpelBean();
		String[] strings = new String[]{"1", "2", "4", "4"};
		List<String> stringList = Arrays.asList(strings);
		Set<String> stringSet = new HashSet<>(stringList);
		Map<Object, Object> objectObjectMap = new HashMap<>();
		objectObjectMap.put("1", 11);
		objectObjectMap.put(2, 22);
		objectObjectMap.put(3, 33);
		objectObjectMap.put("4", Optional.empty());
		Properties properties = new Properties();
		properties.setProperty("1", "111");
		properties.setProperty("2", "222");
		properties.setProperty("3", "333");
		properties.setProperty("4", "444");
		spelBean.setStrings(strings);
		spelBean.setStringList(stringList);
		spelBean.setStringSet(stringSet);
		spelBean.setObjectObjectMap(objectObjectMap);
		spelBean.setProperties(properties);
		ExpressionParser parser = new SpelExpressionParser();
		StandardEvaluationContext context = new StandardEvaluationContext();
		context.setVariable("spelBean", spelBean);

		/*
		 * array和Collection的筛选
		 * array和Collection的[]中的#this表示集合元素本身
		 * 访问集合元素的属性、方法时，可以省略#this引用前缀
		 */

		//直接使用集合
		System.out.println(parser.parseExpression("#this.?[#this.equals('4')]").getValue(strings, List.class));
		//直接使用对象的集合  省略引用元素的#this前缀
		System.out.println(parser.parseExpression("stringList.?[equals('2')]").getValue(spelBean));
		//容器的变量中的集合
		System.out.println(parser.parseExpression("#spelBean.strings.?[#this.equals('4')]").getValue(context));

		System.out.println(parser.parseExpression("#spelBean.stringSet.?[T(Integer).parseInt(#this) < 3]").getValue(context));

		/*
		 * map的筛选
		 * map的[]中的#this同样表示集合元素,不过是Map.Entry
		 * 我们可以获取key或者value进行筛选
		 * 访问集合元素的属性、方法时，可以省略#this引用前缀
		 */
		//直接使用map 根据key筛选
		System.out.println(parser.parseExpression("#this.?[T(Integer).parseInt(#this.key)<3]").getValue(objectObjectMap));
		//直接使用map 根据value筛选  省略引用Entry元素的#this前缀
		System.out.println(parser.parseExpression("#this.?[T(Integer).parseInt(value)<300]").getValue(properties));
		//直接使用对象的map  省略引用元素的#this前缀
		System.out.println(parser.parseExpression("objectObjectMap.?[key.equals('4')]").getValue(spelBean));
		//容器的变量中使用map
		System.out.println(parser.parseExpression("#spelBean.properties.?[key.equals('3')]").getValue(context));


		System.out.println("检索第一个匹配的元素");
		//map是无序的
		System.out.println(parser.parseExpression("#this.^[true]").getValue(properties));
		System.out.println(parser.parseExpression("#this.^[true]").getValue(objectObjectMap));
		//list是有序的
		System.out.println(parser.parseExpression("#this.^[true]").getValue(stringList));
		System.out.println("检索最后一个匹配的元素");
		System.out.println(parser.parseExpression("#this.$[true]").getValue(properties));
		System.out.println(parser.parseExpression("#this.$[true]").getValue(objectObjectMap));
		System.out.println(parser.parseExpression("#this.$[true]").getValue(stringSet));
		System.out.println(parser.parseExpression("#this.$[true]").getValue(strings));
	}

	/**
	 * SPEL支持集合投影（Collection Projection）。简单的说，就是对集合元素进行计算，根据返回的结果生成新的集合，这类似于Java8的Stream的map操作。
	 * <p>
	 * 集合投影语法格式如下：.![projectionExpression]。
	 * projectionExpression必须返回一个结果，返回的结果将整合为一个list列表。
	 * array和Collection的[]中的#this表示集合元素本身，map的[]中的#this同样表示集合元素，不过是Map.Entry，我们可以获取key或者value进行筛选。访问集合元素的属性、方法时，可以省略#this引用前缀
	 */
	@Test
	public void collectionProjection() {
		SpelBean spelBean = new SpelBean();
		String[] strings = new String[]{"a", "b", "c", "d"};
		List<String> stringList = Arrays.asList(strings);
		Set<String> stringSet = new HashSet<>(stringList);
		Map<Object, Object> objectObjectMap = new HashMap<>();
		objectObjectMap.put("1", 11);
		objectObjectMap.put(2, 22);
		objectObjectMap.put(3, 33);
		objectObjectMap.put("4", Optional.empty());
		Properties properties = new Properties();
		properties.setProperty("1", "111");
		properties.setProperty("2", "222");
		properties.setProperty("3", "333");
		properties.setProperty("4", "444");
		spelBean.setStrings(strings);
		spelBean.setStringList(stringList);
		spelBean.setStringSet(stringSet);
		spelBean.setObjectObjectMap(objectObjectMap);
		spelBean.setProperties(properties);
		ExpressionParser parser = new SpelExpressionParser();
		StandardEvaluationContext context = new StandardEvaluationContext();
		context.setVariable("spelBean", spelBean);

		/*array和Collection的投影*/

		//直接使用集合 省略引用元素的#this前缀
		System.out.println(parser.parseExpression("#this.![equals('B')]").getValue(strings, List.class));
		//直接使用对象的集合
		System.out.println(parser.parseExpression("stringList.![#this+'--']").getValue(spelBean));
		//容器的变量中的集合
		System.out.println(parser.parseExpression("#spelBean.strings.![toUpperCase()]").getValue(context, List.class));

		System.out.println(parser.parseExpression("#spelBean.stringSet.![toUpperCase()]").getValue(context));

		/*map的投影*/
		//直接使用map 根据key筛选
		System.out.println(parser.parseExpression("#this.![T(Integer).parseInt(#this.key)+3]").getValue(objectObjectMap));

		//直接使用map 根据value筛选  省略引用Entry元素的#this前缀
		System.out.println(parser.parseExpression("#this.![T(Integer).parseInt(value)+300]").getValue(properties));
		//直接使用对象的map  省略引用元素的#this前缀
		System.out.println(parser.parseExpression("objectObjectMap.![key.equals('4')]").getValue(spelBean));
		//容器的变量中使用map
		System.out.println(parser.parseExpression("#spelBean.properties.![key.equals('3')]").getValue(context));
	}

	/**
	 * 表达式模板（Expression templates）允许将文本与一个或多个计算表达式组合在一起。每个计算表达式都用可定义的前缀和后缀字符分隔，常见的选
	 * 择是使用“#{”作为表达式的前缀，“}”作为表达式的后缀，中间的部分就是计算表达式。
	 * <p>
	 * 在计算时我们需要传递一个ParserContext接口的实例，用于定义表达式模版的解析方式，即表达式的前缀和后缀样式。Spring为我们提供了默认实现
	 * TemplateParserContext以及ParserContext.TEMPLATE_EXPRESSION，它们都是默认以#{}分隔计算表达式的。
	 * <p>
	 * 对所有表达式计算完毕之后,将会把结果填充到原计算表达式的位置，和文本拼接起来，然后一起输出。
	 */
	@Test
	public void expressionTemplates() {
		ExpressionParser parser = new SpelExpressionParser();
		ParserContext parserContext = ParserContext.TEMPLATE_EXPRESSION;
		System.out.println(parser.parseExpression("My First Expression Template name is #{#this}",
				parserContext).getValue(
				"Gaia"));

		System.out.println(parser.parseExpression("random number is #{T(java.lang.Math).random()}",
				parserContext).getValue());

		System.out.println(parser.parseExpression("There are two random numbers: #{T(java.lang.Math).random()},#{T(java.lang.Math).random()}",
				parserContext).getValue());

		System.out.println("-----复杂模版-----");

		StandardEvaluationContext context = new StandardEvaluationContext();
		context.setVariable("num", 5);
		context.setVariable("cost", 3214);
		context.setVariable("unitPrice", 5000);
		context.setVariable("totalPrices", 0);

		System.out.println(parser.parseExpression("The total price before the calculation: #{#totalPrices}", parserContext).getValue(context));
		System.out.println(parser.parseExpression("The car cost #{#cost}, sold #{#num} units, sold at a unit price of " +
						"#{#unitPrice}, and delivered revenue of #{#totalPrices=(#unitPrice - #cost)*#num}.",
				parserContext).getValue(context));

		System.out.println(parser.parseExpression("The calculated total price: #{#totalPrices}", parserContext).getValue(context));
	}

	@Test
	public void spelXml() {
		ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("beans/spring-config.xml");
		System.out.println(ac.getBean("birth", Birth.class));
		System.out.println(ac.getBean("spelBean", SpelBean.class));
	}

	@Test
	public void spelAnnotation () {
		AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AnnotationDemo.class);
		System.out.println(ac.getBean("annotationDemo", AnnotationDemo.class));
		System.out.println(ac.getBean("annotationDemo2", AnnotationDemo2.class));
	}


}

